Also port the testsuite.
gtk_selection_model_unselect_range
gtk_selection_model_select_all
gtk_selection_model_unselect_all
+gtk_selection_model_set_selection
GtkSelectionCallback
gtk_selection_model_select_callback
gtk_selection_model_unselect_callback
}
static gboolean
-gtk_multi_selection_select_range (GtkSelectionModel *model,
- guint position,
- guint n_items,
- gboolean exclusive)
+gtk_multi_selection_set_selection (GtkSelectionModel *model,
+ GtkBitset *selected,
+ GtkBitset *mask)
{
GtkMultiSelection *self = GTK_MULTI_SELECTION (model);
- guint min = G_MAXUINT;
- guint max = 0;
+ GtkBitset *changes;
+ guint min, max, n_items;
- if (exclusive)
- {
- min = gtk_bitset_get_minimum (self->selected);
- max = gtk_bitset_get_maximum (self->selected);
- gtk_bitset_remove_all (self->selected);
- }
-
- gtk_bitset_add_range (self->selected, position, n_items);
+ /* changes = (self->selected XOR selected) AND mask
+ * But doing it this way avoids looking at all values outside the mask
+ */
+ changes = gtk_bitset_copy (selected);
+ gtk_bitset_difference (changes, self->selected);
+ gtk_bitset_intersect (changes, mask);
- min = MIN (position, min);
- max = MAX (max, position + n_items - 1);
+ min = gtk_bitset_get_minimum (changes);
+ max = gtk_bitset_get_maximum (changes);
- gtk_selection_model_selection_changed (model, min, max - min + 1);
+ /* sanity check */
+ n_items = g_list_model_get_n_items (self->model);
+ if (max >= n_items)
+ {
+ gtk_bitset_remove_range_closed (changes, n_items, max);
+ max = gtk_bitset_get_maximum (changes);
+ }
- return TRUE;
-}
+ /* actually do the change */
+ gtk_bitset_difference (self->selected, changes);
-static gboolean
-gtk_multi_selection_unselect_range (GtkSelectionModel *model,
- guint position,
- guint n_items)
-{
- GtkMultiSelection *self = GTK_MULTI_SELECTION (model);
+ gtk_bitset_unref (changes);
- gtk_bitset_remove_range (self->selected, position, n_items);
- gtk_selection_model_selection_changed (model, position, n_items);
+ if (min <= max)
+ gtk_selection_model_selection_changed (model, min, max - min + 1);
return TRUE;
}
-static gboolean
-gtk_multi_selection_select_item (GtkSelectionModel *model,
- guint position,
- gboolean exclusive)
-{
- return gtk_multi_selection_select_range (model, position, 1, exclusive);
-}
-
-static gboolean
-gtk_multi_selection_unselect_item (GtkSelectionModel *model,
- guint position)
-{
- return gtk_multi_selection_unselect_range (model, position, 1);
-}
-
-static gboolean
-gtk_multi_selection_select_all (GtkSelectionModel *model)
-{
- return gtk_multi_selection_select_range (model, 0, g_list_model_get_n_items (G_LIST_MODEL (model)), FALSE);
-}
-
-static gboolean
-gtk_multi_selection_unselect_all (GtkSelectionModel *model)
-{
- return gtk_multi_selection_unselect_range (model, 0, g_list_model_get_n_items (G_LIST_MODEL (model)));
-}
-
static gboolean
gtk_multi_selection_add_or_remove (GtkSelectionModel *model,
gboolean unselect_rest,
gtk_multi_selection_selection_model_init (GtkSelectionModelInterface *iface)
{
iface->is_selected = gtk_multi_selection_is_selected;
- iface->select_item = gtk_multi_selection_select_item;
- iface->unselect_item = gtk_multi_selection_unselect_item;
- iface->select_range = gtk_multi_selection_select_range;
- iface->unselect_range = gtk_multi_selection_unselect_range;
- iface->select_all = gtk_multi_selection_select_all;
- iface->unselect_all = gtk_multi_selection_unselect_all;
+ iface->set_selection = gtk_multi_selection_set_selection;
iface->select_callback = gtk_multi_selection_select_callback;
iface->unselect_callback = gtk_multi_selection_unselect_callback;
}
}
static gboolean
-gtk_property_selection_select_range (GtkSelectionModel *model,
- guint position,
- guint n_items,
- gboolean exclusive)
+gtk_property_selection_set_selection (GtkSelectionModel *model,
+ GtkBitset *selected,
+ GtkBitset *mask)
{
GtkPropertySelection *self = GTK_PROPERTY_SELECTION (model);
- guint i;
- guint n;
+ GtkBitsetIter iter;
+ guint i, n, min, max;
+ gboolean has_value;
n = g_list_model_get_n_items (G_LIST_MODEL (self));
- if (exclusive)
+ min = G_MAXUINT;
+ max = 0;
+
+ for (has_value = gtk_bitset_iter_init_first (&iter, mask, &i);
+ has_value && i < n;
+ has_value = gtk_bitset_iter_next (&iter, &i))
{
- for (i = 0; i < n; i++)
- set_selected (self, i, FALSE);
+ set_selected (self, i, gtk_bitset_contains (selected, i));
+ /* XXX: Check if something changed? */
+ min = MIN (min, i);
+ max = MAX (max, i);
}
- for (i = position; i < position + n_items; i++)
- set_selected (self, i, TRUE);
-
- /* FIXME: do better here */
- if (exclusive)
- gtk_selection_model_selection_changed (model, 0, n);
- else
- gtk_selection_model_selection_changed (model, position, n_items);
-
- return TRUE;
-}
-
-static gboolean
-gtk_property_selection_unselect_range (GtkSelectionModel *model,
- guint position,
- guint n_items)
-{
- GtkPropertySelection *self = GTK_PROPERTY_SELECTION (model);
- guint i;
-
- for (i = position; i < position + n_items; i++)
- set_selected (self, i, FALSE);
-
- gtk_selection_model_selection_changed (model, position, n_items);
+ if (min <= max)
+ gtk_selection_model_selection_changed (model, min, max - min + 1);
return TRUE;
}
-static gboolean
-gtk_property_selection_select_item (GtkSelectionModel *model,
- guint position,
- gboolean exclusive)
-{
- return gtk_property_selection_select_range (model, position, 1, exclusive);
-}
-
-static gboolean
-gtk_property_selection_unselect_item (GtkSelectionModel *model,
- guint position)
-{
- return gtk_property_selection_unselect_range (model, position, 1);
-}
-
-static gboolean
-gtk_property_selection_select_all (GtkSelectionModel *model)
-{
- return gtk_property_selection_select_range (model, 0, g_list_model_get_n_items (G_LIST_MODEL (model)), FALSE);
-}
-
-static gboolean
-gtk_property_selection_unselect_all (GtkSelectionModel *model)
-{
- return gtk_property_selection_unselect_range (model, 0, g_list_model_get_n_items (G_LIST_MODEL (model)));
-}
-
static gboolean
gtk_property_selection_add_or_remove (GtkSelectionModel *model,
gboolean unselect_rest,
gtk_property_selection_selection_model_init (GtkSelectionModelInterface *iface)
{
iface->is_selected = gtk_property_selection_is_selected;
- iface->select_item = gtk_property_selection_select_item;
- iface->unselect_item = gtk_property_selection_unselect_item;
- iface->select_range = gtk_property_selection_select_range;
- iface->unselect_range = gtk_property_selection_unselect_range;
- iface->select_all = gtk_property_selection_select_all;
- iface->unselect_all = gtk_property_selection_unselect_all;
+ iface->set_selection = gtk_property_selection_set_selection;
iface->select_callback = gtk_property_selection_select_callback;
iface->unselect_callback = gtk_property_selection_unselect_callback;
}
guint position,
gboolean unselect_rest)
{
- return FALSE;
+ GtkBitset *selected;
+ GtkBitset *mask;
+ gboolean result;
+
+ selected = gtk_bitset_new_empty ();
+ gtk_bitset_add (selected, position);
+ if (unselect_rest)
+ {
+ mask = gtk_bitset_new_empty ();
+ gtk_bitset_add_range (mask, 0, g_list_model_get_n_items (G_LIST_MODEL (model)));
+ }
+ else
+ {
+ mask = gtk_bitset_ref (selected);
+ }
+
+ result = gtk_selection_model_set_selection (model, selected, mask);
+
+ gtk_bitset_unref (selected);
+ gtk_bitset_unref (mask);
+
+ return result;
}
+
static gboolean
gtk_selection_model_default_unselect_item (GtkSelectionModel *model,
guint position)
{
- return FALSE;
+ GtkBitset *selected;
+ GtkBitset *mask;
+ gboolean result;
+
+ selected = gtk_bitset_new_empty ();
+ mask = gtk_bitset_new_empty ();
+ gtk_bitset_add (mask, position);
+
+ result = gtk_selection_model_set_selection (model, selected, mask);
+
+ gtk_bitset_unref (selected);
+ gtk_bitset_unref (mask);
+
+ return result;
}
static gboolean
guint n_items,
gboolean unselect_rest)
{
- return FALSE;
+ GtkBitset *selected;
+ GtkBitset *mask;
+ gboolean result;
+
+ selected = gtk_bitset_new_empty ();
+ gtk_bitset_add_range (selected, position, n_items);
+ if (unselect_rest)
+ {
+ mask = gtk_bitset_new_empty ();
+ gtk_bitset_add_range (mask, 0, g_list_model_get_n_items (G_LIST_MODEL (model)));
+ }
+ else
+ {
+ mask = gtk_bitset_ref (selected);
+ }
+
+ result = gtk_selection_model_set_selection (model, selected, mask);
+
+ gtk_bitset_unref (selected);
+ gtk_bitset_unref (mask);
+
+ return result;
}
static gboolean
guint position,
guint n_items)
{
- return FALSE;
+ GtkBitset *selected;
+ GtkBitset *mask;
+ gboolean result;
+
+ selected = gtk_bitset_new_empty ();
+ mask = gtk_bitset_new_empty ();
+ gtk_bitset_add_range (mask, position, n_items);
+
+ result = gtk_selection_model_set_selection (model, selected, mask);
+
+ gtk_bitset_unref (selected);
+ gtk_bitset_unref (mask);
+
+ return result;
}
static gboolean
return gtk_selection_model_unselect_range (model, 0, g_list_model_get_n_items (G_LIST_MODEL (model)));
}
+static gboolean
+gtk_selection_model_default_set_selection (GtkSelectionModel *model,
+ GtkBitset *selected,
+ GtkBitset *mask)
+{
+ return FALSE;
+}
+
static void
gtk_selection_model_default_init (GtkSelectionModelInterface *iface)
{
iface->unselect_range = gtk_selection_model_default_unselect_range;
iface->select_all = gtk_selection_model_default_select_all;
iface->unselect_all = gtk_selection_model_default_unselect_all;
+ iface->set_selection = gtk_selection_model_default_set_selection;
iface->select_callback = gtk_selection_model_default_select_callback;
iface->unselect_callback = gtk_selection_model_default_unselect_callback;
* @unselect_rest: whether previously selected items should be unselected
*
* Requests to select an item in the model.
+ *
+ * Returns: %TRUE if this action was supported and no fallback should be
+ * tried. This does not mean the item was selected.
*/
gboolean
gtk_selection_model_select_item (GtkSelectionModel *model,
{
GtkSelectionModelInterface *iface;
- g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0);
+ g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), FALSE);
iface = GTK_SELECTION_MODEL_GET_IFACE (model);
return iface->select_item (model, position, unselect_rest);
* @position: the position of the item to unselect
*
* Requests to unselect an item in the model.
+ *
+ * Returns: %TRUE if this action was supported and no fallback should be
+ * tried. This does not mean the item was unselected.
*/
gboolean
gtk_selection_model_unselect_item (GtkSelectionModel *model,
{
GtkSelectionModelInterface *iface;
- g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0);
+ g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), FALSE);
iface = GTK_SELECTION_MODEL_GET_IFACE (model);
return iface->unselect_item (model, position);
* @unselect_rest: whether previously selected items should be unselected
*
* Requests to select a range of items in the model.
+ *
+ * Returns: %TRUE if this action was supported and no fallback should be
+ * tried. This does not mean the range was selected.
*/
gboolean
gtk_selection_model_select_range (GtkSelectionModel *model,
{
GtkSelectionModelInterface *iface;
- g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0);
+ g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), FALSE);
iface = GTK_SELECTION_MODEL_GET_IFACE (model);
return iface->select_range (model, position, n_items, unselect_rest);
* @n_items: the number of items to unselect
*
* Requests to unselect a range of items in the model.
+ *
+ * Returns: %TRUE if this action was supported and no fallback should be
+ * tried. This does not mean the range was unselected.
*/
gboolean
gtk_selection_model_unselect_range (GtkSelectionModel *model,
{
GtkSelectionModelInterface *iface;
- g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0);
+ g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), FALSE);
iface = GTK_SELECTION_MODEL_GET_IFACE (model);
return iface->unselect_range (model, position, n_items);
* @model: a #GtkSelectionModel
*
* Requests to select all items in the model.
+ *
+ * Returns: %TRUE if this action was supported and no fallback should be
+ * tried. This does not mean that all items are now selected.
*/
gboolean
gtk_selection_model_select_all (GtkSelectionModel *model)
{
GtkSelectionModelInterface *iface;
- g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0);
+ g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), FALSE);
iface = GTK_SELECTION_MODEL_GET_IFACE (model);
return iface->select_all (model);
* @model: a #GtkSelectionModel
*
* Requests to unselect all items in the model.
+ *
+ * Returns: %TRUE if this action was supported and no fallback should be
+ * tried. This does not mean that all items are now unselected.
*/
gboolean
gtk_selection_model_unselect_all (GtkSelectionModel *model)
{
GtkSelectionModelInterface *iface;
- g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0);
+ g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), FALSE);
iface = GTK_SELECTION_MODEL_GET_IFACE (model);
return iface->unselect_all (model);
}
+/**
+ * gtk_selection_model_set_selection:
+ * @model: a #GtkSelectionModel
+ * @selected: bitmask specifying if items should be selected or
+ * unselected
+ * @mask: bitmask specifying which items should be updated
+ *
+ * This is the most advanced selection updating method that allows
+ * the most fine-grained control over selection changes.
+ * If you can, you should try the simpler versions, as implementations
+ * are more likely to implement support for those.
+ *
+ * Requests that the selection state of all positions set in @mask be
+ * updated to the respecitve value in the @selected bitmask.
+ *
+ * In pseudocode, it would look something like this:
+ *
+ * |[<!-- language="C" -->
+ * for (i = 0; i < n_items; i++)
+ * {
+ * // don't change values not in the mask
+ * if (!gtk_bitset_contains (mask, i))
+ * continue;
+ *
+ * if (gtk_bitset_contains (selected, i))
+ * select_item (i);
+ * else
+ * unselect_item (i);
+ * }
+ *
+ * gtk_selection_model_selection_changed (model, first_changed_item, n_changed_items);
+ * ]|
+ *
+ * @mask and @selected must not be modified. They may refer to the same bitset,
+ * which would mean that every item in the set should be selected.
+ *
+ * Returns: %TRUE if this action was supported and no fallback should be
+ * tried. This does not mean that all items were updated according
+ * to the inputs.
+ **/
+gboolean
+gtk_selection_model_set_selection (GtkSelectionModel *model,
+ GtkBitset *selected,
+ GtkBitset *mask)
+{
+ GtkSelectionModelInterface *iface;
+
+ g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), FALSE);
+ g_return_val_if_fail (selected != NULL, FALSE);
+ g_return_val_if_fail (mask != NULL, FALSE);
+
+ iface = GTK_SELECTION_MODEL_GET_IFACE (model);
+ return iface->set_selection (model, selected, mask);
+}
+
/**
* gtk_selection_model_select_callback:
* @model: a #GtkSelectionModel
* unsupported or known to fail for all items, return %FALSE.
* @unselect_all: Unselect all items in the model. If the operation is
* unsupported or known to fail for all items, return %FALSE.
+ * @set_selection: Set selection state of all items in mask to selected.
+ * See gtk_selection_model_set_selection() for a detailed explanation
+ * of this function.
*
* The list of virtual functions for the #GtkSelectionModel interface.
* No function must be implemented, but unless #GtkSelectionModel::is_selected()
* selecting or unselecting items. Of course, if the model does not do that,
* it means that users cannot select or unselect items in a list widget
* using the model.
+ *
+ * All selection functions fall back to #GtkSelectionModel::set_selection()
+ * so it is sufficient to implement just that function for full selection
+ * support.
*/
struct _GtkSelectionModelInterface
{
guint n_items);
gboolean (* select_all) (GtkSelectionModel *model);
gboolean (* unselect_all) (GtkSelectionModel *model);
+ gboolean (* set_selection) (GtkSelectionModel *model,
+ GtkBitset *selected,
+ GtkBitset *mask);
gboolean (* select_callback) (GtkSelectionModel *model,
gboolean unselect_rest,
GtkSelectionCallback callback,
gboolean gtk_selection_model_select_all (GtkSelectionModel *model);
GDK_AVAILABLE_IN_ALL
gboolean gtk_selection_model_unselect_all (GtkSelectionModel *model);
+GDK_AVAILABLE_IN_ALL
+gboolean gtk_selection_model_set_selection (GtkSelectionModel *model,
+ GtkBitset *selected,
+ GtkBitset *mask);
GDK_AVAILABLE_IN_ALL
gboolean gtk_selection_model_select_callback (GtkSelectionModel *model,
ret = gtk_selection_model_select_range (selection, 3, 2, FALSE);
g_assert_true (ret);
assert_selection (selection, "3 4 5");
- assert_selection_changes (selection, "3:2");
+ assert_selection_changes (selection, "4:1");
ret = gtk_selection_model_select_range (selection, 0, 1, TRUE);
g_assert_true (ret);
g_object_unref (selection);
}
-typedef struct {
- guint start;
- guint n;
- gboolean in;
-} SelectionData;
-
-static void
-select_some (guint position,
- guint *start,
- guint *n,
- gboolean *selected,
- gpointer data)
-{
- SelectionData *sdata = data;
- guint i;
-
- for (i = 0; sdata[i].n != 0; i++)
- {
- if (sdata[i].start <= position &&
- position < sdata[i].start + sdata[i].n)
- break;
- }
-
- *start = sdata[i].start;
- *n = sdata[i].n;
- *selected = sdata[i].in;
-}
-
static void
-test_callback (void)
+test_set_selection (void)
{
GtkSelectionModel *selection;
gboolean ret;
GListStore *store;
- SelectionData data[] = {
- { 0, 2, FALSE },
- { 2, 3, TRUE },
- { 5, 2, FALSE },
- { 6, 3, TRUE },
- { 9, 1, FALSE },
- { 0, 0, FALSE }
- };
-
- SelectionData more_data[] = {
- { 0, 3, FALSE },
- { 3, 1, TRUE },
- { 4, 3, FALSE },
- { 7, 1, TRUE },
- { 0, 0, FALSE }
- };
+ GtkBitset *selected, *mask;
store = new_store (1, 10, 1);
assert_selection (selection, "");
assert_selection_changes (selection, "");
- ret = gtk_selection_model_select_callback (selection, FALSE, select_some, data);
+ selected = gtk_bitset_new_empty ();
+ gtk_bitset_add_range (selected, 2, 3);
+ gtk_bitset_add_range (selected, 6, 3);
+ mask = gtk_bitset_new_empty ();
+ gtk_bitset_add_range (mask, 0, 100); /* too big on purpose */
+ ret = gtk_selection_model_set_selection (selection, selected, mask);
g_assert_true (ret);
+ gtk_bitset_unref (selected);
+ gtk_bitset_unref (mask);
assert_selection (selection, "3 4 5 7 8 9");
assert_selection_changes (selection, "2:7");
- ret = gtk_selection_model_unselect_callback (selection, select_some, more_data);
+ selected = gtk_bitset_new_empty ();
+ mask = gtk_bitset_new_empty ();
+ gtk_bitset_add (mask, 3);
+ gtk_bitset_add (mask, 7);
+ ret = gtk_selection_model_set_selection (selection, selected, mask);
g_assert_true (ret);
+ gtk_bitset_unref (selected);
+ gtk_bitset_unref (mask);
assert_selection (selection, "3 5 7 9");
assert_selection_changes (selection, "3:5");
g_test_add_func ("/multiselection/selection", test_selection);
g_test_add_func ("/multiselection/select-range", test_select_range);
g_test_add_func ("/multiselection/readd", test_readd);
- g_test_add_func ("/multiselection/callback", test_callback);
+ g_test_add_func ("/multiselection/set_selection", test_set_selection);
return g_test_run ();
}